/* Copyright (c) 2024 Huawei Technologies Co., Ltd.
openFuyao is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN 'AS IS' BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

import BreadCrumbCom from '@/components/BreadCrumbCom';
import zhCN from 'antd/es/locale/zh_CN';
import '@/styles/affinityPolicyConfiguration.less';
import { QuestionCircleOutlined, ExclamationCircleFilled } from '@ant-design/icons';
import { Radio, Switch, message, ConfigProvider, Table, Collapse, Space, Modal, Button, Drawer, theme } from 'antd';
import { useEffect, useState } from 'openinula';
import noData from '@/assets/images/noData.png';
import PriorityPreemption from '@/pages/SchedulingPolicyConfiguration/PriorityPreemption';
import { volcano, distance, runningPod } from '@/assets/md';
import { ResponseCode } from '@/common/constants';
import {
  getNumaAffinitySchedulingStatus,
  updateNumaAffinitySchedulingStatus,
  getNumaDistanceSchedulingStatus,
  updateNumaDistanceSchedulingStatus,
  getPodStatus,
  updatePodStatus,
  getBalanceStatus,
  updateBalanceStatus,
} from '@/api/schedulingApi';
import { getNodeList, normalizeKeys, getCR } from '@/api/numaApi';
import { sorterFirstAlphabet } from '@/tools/utils';
import { filterRepeat } from '@/tools/utils';

export default function AffinityPolicyConfiguration() {
  const [affinityData, setAffinityData] = useState([]);
  const [numaAffKey, setNumaAffKey] = useState();
  const [descData, setDescData] = useState();
  const [className, setClassName] = useState('');
  const [numaChecked, setNumaChecked] = useState(false);
  const [numaDistanceChecked, setNumaDistanceChecked] = useState(false);
  const [podChecked, setPodChecked] = useState(false);
  const [balanceChecked, setBalanceChecked] = useState(false);
  const [afinityFilter, setAfinityFilter] = useState();
  const [modalInfoOpen, setModalInfoOpen] = useState(false);
  const [modalType, setModalType] = useState('');
  const [kubeletVersionWarn, setKubeletVersionWarn] = useState(false);
  const affinityColumns = [
    {
      title: '节点名称',
      dataIndex: 'name',
      key: 'name',
      sorter: (a, b) => sorterFirstAlphabet(a.name, b.name),
      render: (_, record) =>
        record.name,
    },
    {
      title: '拓扑策略',
      dataIndex: 'topologyPolicy',
      key: 'topologyPolicy',
      filters: afinityFilter,
      filteredValue: affinityData.topologyPolicy,
      onFilter: (value, record) => record.topologyPolicy.includes(value),
      sorter: (a, b) => sorterFirstAlphabet(a.topologyPolicy, b.topologyPolicy),
      render: (_, record) => record.topologyPolicy,
    },
  ];

  const affinityitems = [
    {
      key: 'nodeTopologyStrategy',
      label: '节点拓扑策略',
      children: <div className='volcano-table' style={{ padding: 0 }}>
        <ConfigProvider locale={zhCN}>
          <Table
            dataSource={affinityData}
            columns={affinityColumns}
            pagination={{
              className: 'volcano-page',
              pageSizeOptions: [10, 20, 50],
              showSizeChanger: true,
              showQuickJumper: true,
              showTotal: total => `共${total}条`,
            }}
            style={{ marginBottom: '20px' }} />
        </ConfigProvider>
      </div>,
    },
  ];
  const [distanceData, setDistanceData] = useState([
    {
      name: 'numa1',
      num: '2',
      memory: ['pad1', 'pado22'],
      net: ['pad1', 'pado22'],
    },
  ]);
  const [distanceCurrent, setDistanceCurrent] = useState(1);
  const distanceColumns = [
    {
      title: 'NUMA名称',
      dataIndex: 'name',
      key: 'name',
      sorter: (a, b) => sorterFirstAlphabet(a.name, b.name),
      render: (_, record) => (
        <Space size="middle" className="">{record.name}</Space>
      ),
    },
    {
      title: '亲和Pod对数',
      dataIndex: 'num',
      key: 'num',
      sorter: (a, b) => sorterFirstAlphabet(a.num, b.num),
      render: (_, record) => (
        <Space size="middle" className="">{record.num}</Space>
      ),
    },
    {
      title: '内存亲和Pod名称',
      dataIndex: 'memoryName',
      key: 'memoryName',
      render: (_, record) => (
        <div>
          {record.memory.map((item, index) => (
            <p>{item}</p>
          ))}
        </div>
      ),
    },
    {
      title: '网络亲和Pod名称',
      dataIndex: 'netName',
      key: 'netName',
      render: (_, record) => (
        <div>
          {record.net.map((item, index) => (
            <p>{item}</p>
          ))}
        </div>
      ),
    },
  ];

  const handleDistancePageChange = (page, pageSize) => {
    setAffinityCurrent(page); // 更新当前页码
  };
  const distanceItems = [
    {
      key: '1',
      label: '节点拓扑策略',
      children: <div className='volcano-table'>
        <ConfigProvider locale={zhCN}>
          <Table
            dataSource={distanceData}
            columns={distanceColumns}
            pagination={{
              className: 'volcano-page',
              current: distanceCurrent,
              pageSizeOptions: [10, 20, 50],
              showSizeChanger: true,
              showQuickJumper: true,
              onChange: handleDistancePageChange,
              showTotal: total => `共${dataSource.length}条`,
            }}
            style={{ marginBottom: '20px' }} />
        </ConfigProvider>
      </div>,
    },
  ];
  const { token } = theme.useToken();
  const [open, setOpen] = useState(false);
  const showDrawer = (data, temproryClass) => {
    setDescData(data);
    setClassName(temproryClass);
    setOpen(true);
  };
  const onClose = () => {
    setOpen(false);
  };

  const handleSkip = () => {
    setKubeletVersionWarn(false);
    showDrawer(distance);
  };

  const handleNumaChange = async (e) => {
    if (e) {
      setModalInfoOpen(true);
      setModalType('numa');
    } else {
      let obj = { policy: '' };
      obj.policy = 'close';
      const res = await updateNumaAffinitySchedulingStatus(obj);
      if (res.status === ResponseCode.OK) {
        setNumaChecked(false);
      }
    }
  };

  const handleConfirmInfo = async () => {
    if (modalType === 'numa') {
      let obj = { policy: '' };
      obj.policy = 'open';
      const res = await updateNumaAffinitySchedulingStatus(obj);
      if (res.status === ResponseCode.OK) {
        setNumaChecked(true);
        setModalInfoOpen(false);
      }
    }
    if (modalType === 'numaDistance') {
      let obj = { policy: '' };
      obj.policy = 'open';
      try {
        const res = await updateNumaDistanceSchedulingStatus(obj);
        if (res.status === ResponseCode.OK) {
          setNumaDistanceChecked(true);
          setModalInfoOpen(false);
        }
      } catch (e) {
        if (e.response.data.code === ResponseCode.NotAccept) {
          setModalInfoOpen(false);
          setKubeletVersionWarn(true);
        }
      }
    }
  };

  const handleNumaDistanceChange = async (e) => {
    if (e) {
      setModalInfoOpen(true);
      setModalType('numaDistance');
    } else {
      let obj = { policy: '' };
      obj.policy = 'close';
      const res = await updateNumaDistanceSchedulingStatus(obj);
      if (res.status === ResponseCode.OK) {
        setNumaDistanceChecked(e);
      }
    }
  };

  const handlePodChange = async (e) => {
    let obj = { policy: '' };
    setModalType('pod');
    e ? obj.policy = 'open' : obj.policy = 'close';
    const res = await updatePodStatus(obj);
    if (res.status === ResponseCode.OK) {
      setPodChecked(e);
    }
  };

  const handleBalanceChange = async (e) => {
    let obj = { policy: '' };
    setModalType('balance');
    e ? obj.policy = 'open' : obj.policy = 'close';
    const res = await updateBalanceStatus(obj);
    if (res.status === ResponseCode.OK) {
      setBalanceChecked(e);
    }
  };

  const hanldeNumaAff = async (e) => {
    setNumaAffKey(e);
    if (e && e.length && e.includes('nodeTopologyStrategy')) {
      const res = await getNodeList();
      if (res.status === ResponseCode.OK) {
        const finalData = await Promise.all(res.data.data.items.map(async nodeItem => {
          const crDetails = await getCR(nodeItem.metadata.name);
          if (crDetails?.data) {
            crDetails.data = normalizeKeys(crDetails.data);
          }
          return {
            name: nodeItem.metadata.name,
            topologyPolicy: crDetails?.data?.data?.spec?.policies?.topologymanagerpolicy || 'N/A',
          };
        }));
        let filterData = [];
        finalData.forEach(item => {
          filterData.push({
            text: item.topologyPolicy,
            value: item.topologyPolicy,
          });
        });
        setAfinityFilter(filterRepeat(filterData));
        setAffinityData(finalData);
      }
    }
  };

  const getNumaStatus = async () => {
    const [
      {
        value: numaRes = null
      },
      {
        value: distanceRes = null,
      },
      { value: podRes = null },
    ] = await Promise.allSettled([
      getNumaAffinitySchedulingStatus(),
      getNumaDistanceSchedulingStatus(),
      getPodStatus(),
    ]);
    if (distanceRes?.status === ResponseCode.NotAccept) {
      setKubeletVersionWarn(true);
    }
    if (numaRes?.status === ResponseCode.OK) {
      setNumaChecked(numaRes.data.data === 'open' ? true : false);
    }
    if (distanceRes?.status === ResponseCode.OK) {
      setNumaDistanceChecked(distanceRes.data.data === 'open' ? true : false);
    }

    if (podRes?.status === ResponseCode.OK) {
      setPodChecked(podRes.data.data === 'open' ? true : false);
    }
  };

  useEffect(() => {
    getNumaStatus();
  }, []);

  return (
    <div id="affinity_policy" className="affinity_policy">
      <BreadCrumbCom items={[
        { title: 'NUMA亲和调度', disabled: true },
        { title: '亲和策略配置', disabled: true },
      ]} />
      <div className="affinity_policy_content">
        <div className="affinity_policy_content_one">
          <div className="affinity_policy_content_one_title">numa-aware
            <QuestionCircleOutlined className='question-icon-hover' onClick={() => showDrawer(volcano)} style={{ marginLeft: '7px' }} />
          </div>
          <Switch className="mb_12" checked={numaChecked} onChange={handleNumaChange} />
          <div className="affinity_policy_content_one_desc">启动该能力后，将guaranteed QOS类别的Pod调度至NUMA亲和的节点上。</div>
          <Collapse activeKey={numaAffKey} items={affinityitems} bordered={false} onChange={hanldeNumaAff} />
        </div>
        <div className="affinity_policy_content_one">
          <div className="affinity_policy_content_one_title">最优NUMA Distance
            <QuestionCircleOutlined className='question-icon-hover' style={{ marginLeft: '7px' }} onClick={() => showDrawer(distance, 'break_code_md_body')} />
          </div>
          <Switch className="mb_12" checked={numaDistanceChecked} onChange={handleNumaDistanceChange} />
          <div className="affinity_policy_content_one_desc">该能力通过Kubernetes配置文件打开，启动该能力后，对于跨NUMA分配CPU资源的Pod，一定分配到物理距离最近的NUMA。</div>
        </div>
        <div className="affinity_policy_content_one">
          <div className="affinity_policy_content_one_title">运行时Pod亲和优化
            <QuestionCircleOutlined className='question-icon-hover' style={{ marginLeft: '7px' }} onClick={() => showDrawer(runningPod, 'break_code_md_body')} />
          </div>
          <Switch className="mb_12" checked={podChecked} onChange={handlePodChange} />
          <div className="affinity_policy_content_one_desc">启动该能力后，在运行时动态对Pod部署位置进行重调度，使得亲和业务调度至同一NUMA上，提升业务性能。</div>
        </div>
        <div className="affinity_policy_content_one" style={{ display: 'none' }}>
          <div className="affinity_policy_content_one_title">均衡调度
          </div>
          <Switch className="mb_12" checked={balanceChecked} onChange={handleBalanceChange} />
          <div className="affinity_policy_content_one_desc">将同类业务Pod尽量调度至不同节点上，降低众核场景下资源竞争。</div>
        </div>
        <Drawer
          title=""
          placement="right"
          closable={false}
          onClose={onClose}
          width={600}
          open={open}
          getContainer={() => document.getElementById('affinity_policy')}
        >
          <PriorityPreemption className={className} onClose={onClose} data={descData} />
        </Drawer>
        <Modal
          className='modal_flex_delete'
          getContainer={false}
          open={modalInfoOpen}
          title='提示'
          footer={() => (
            <>
              <Button className='cancel_btn' onClick={handleConfirmInfo}>确定</Button>
            </>
          )}
          onCancel={() => setModalInfoOpen(false)}
        >
          <div className='modal_delete_content'>
            <ExclamationCircleFilled className='warn_icon' />
            <div className='word_tograry'>
              <div className='word_tograry_alarm'>
                <p className='bread_word_delete'>开启该选项会修改kubelet配置项，需要重启kubelet，</p>
                <p className='bread_word_delete'>可能对已部署业务产生影响，确定重启吗？</p>
              </div>
            </div>
          </div>
        </Modal>
        <Modal
          className='modal_flex_delete'
          getContainer={false}
          title='kubelet版本不支持'
          open={kubeletVersionWarn}
          footer={() => (
            <>
              <Button className='cancel_btn' onClick={() => setKubeletVersionWarn(false)}>确定</Button>
            </>
          )}
          onCancel={() => setKubeletVersionWarn(false)}
        >
          <div className='modal_delete_content'>
            <ExclamationCircleFilled className='warn_icon' />
            <div className='word_tograry'>
              <div className='word_tograry_alarm'>
                <p className='bread_word_delete'>kubelet版本不支持该选项，当前仅支持v1.28.0或以上。</p>
                <p className='bread_word_delete'>请升级kubelet版本或替换kubelet二进制，方法请参考<a onClick={handleSkip}>提示链接</a>。</p>
              </div>
            </div>
          </div>
        </Modal>
      </div>
    </div>
  );
};